package com.hang.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hang.common.BizErrorCodeEnum;
import com.hang.common.BizException;
import com.hang.config.RedisConfig;
import com.hang.mapper.*;
import com.hang.model.*;
import com.hang.service.TaskService;
import com.hang.vo.StatisticClazzVo;
import com.hang.vo.StatisticTaskVo;
import com.hang.vo.TaskDetail;
import com.hang.vo.TaskVo;
import java.time.LocalDateTime;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 *
 */
@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
    implements TaskService{

    @Autowired
    private ClazzStudentMapper clazzStudentMapper;

    @Autowired
    private AttendanceMapper attendanceMapper;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private ClazzMapper clazzMapper;

    @Autowired
    private TeacherMapper teacherMapper;

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private LeaveMapper leaveMapper;

    @Autowired
    private RedisConfig redisConfig;

    /**
     * 发布任务接口
     * @param taskVo
     */
    @Override
    public void saveTask(TaskVo taskVo) {
        if(ObjectUtils.isEmpty(taskVo)){
            throw new BizException(BizErrorCodeEnum.OBJECT_IS_NULL);
        }

        Task task = new Task();
        BeanUtils.copyProperties(taskVo,task);
        //任务开始时肯定实到0人
        task.setRealCount(0);

        if(ObjectUtils.isEmpty(task)){
            throw new BizException(BizErrorCodeEnum.OBJECT_IS_NULL);
        }

        try {
            //插入数据，同时返回任务号，以便任务发送
            taskMapper.insertTask(task);
        }catch (Exception e){
            throw new BizException(BizErrorCodeEnum.TASK_PUSH_FAIL);
        }

        //完成任务推送:添加到考勤表
        pushTaskToClazzStudent(task);
    }

    /**
     * 获取班级对应的任务列表
     * @param clazzId
     * @param page
     * @param size
     * @return
     */
    @Override
    public List<Task> getTaskLists(String clazzId, Integer page, Integer size) {
        if(StringUtils.isBlank(clazzId)){
            throw new BizException(BizErrorCodeEnum.SEARCH_IS_NULL);
        }

        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("clazz_id",clazzId);
        //根据打卡时间排序(最新)
        queryWrapper.orderByDesc("start_time");

        Page<Task> pages = new Page<>(page,size);
        IPage<Task> iPage = taskMapper.selectPage(pages,queryWrapper);

        return iPage.getRecords();
    }

    /**
     * 将该任务推送给该班级的所有学生
     * 先存入数据表中，然后写入缓存【taskId  <-->  Map<String,Integer> 】
     * 用Map去存储对应任务下的对应学生的状态，方便老师后续查看该任务的考勤结果
     *
     * @param task
     */
    private void pushTaskToClazzStudent(Task task){
        //任务-学生信息存入db1
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        String clazzId = task.getClazzId();
        QueryWrapper<ClazzStudent> queryWrapper = new QueryWrapper<>();

        queryWrapper.eq("clazz_id",clazzId);
        List<ClazzStudent> students = clazzStudentMapper.selectList(queryWrapper);

        //将这些学生列表存入考勤表中
        for(ClazzStudent student : students){
            Attendance attendance = new Attendance();
            if(!StringUtils.isBlank(student.getStudentId())){
                attendance.setStudentId(student.getStudentId());
                attendance.setTeacherId(task.getTeacherId());
                attendance.setTaskId(task.getTaskId());
                //开始默认为缺勤状态
                attendance.setStatus(0);

                //存入考勤表
                attendanceMapper.insert(attendance);

                //存入redis中，方便后续读取
                redisTemplate.opsForHash().put(String.valueOf(task.getTaskId()),student.getStudentId(),0);
            }
        }
    }

    @Override
    public boolean hasMoreData(String clazzId, Integer page, Integer size) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("clazz_id",clazzId);

        int totals = taskMapper.selectCount(queryWrapper);

        return totals>page*size ? Boolean.TRUE:Boolean.FALSE;
    }

    /**
     * 获取任务的详尽信息
     *
     * 首先先去查看redis中是否有对应任务
     *      如果有，则在redis中读取
     *      如果没有，则先读数据库，再写缓存
     *
     *
     * @param taskId
     * @param status
     * @return
     */
    @Override
    public Map<String, Object> getDetails(Long taskId, Integer status) {
        if(ObjectUtils.isEmpty(taskId)){
            throw new BizException(BizErrorCodeEnum.ID_IS_NULL);
        }
        Map<String,Object> map = new HashMap<>();

        Task task = taskMapper.selectById(taskId);

        if(ObjectUtils.isEmpty(task)){
            throw new BizException(BizErrorCodeEnum.CLAZZ_FOUND_FAIL);
        }

        map.put("name",task.getName());
        map.put("accessCount",task.getAccessCount());
        map.put("startTime",task.getStartTime());
        map.put("endTime",task.getEndTime());

        //选择1号数据库
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        Map<String, Integer> entries = redisTemplate.<String,Integer>opsForHash().entries(String.valueOf(taskId));

        //学生列表
        List<Student> students = new ArrayList<>();
        int realCount = 0;
        int leaveCount = 0;

        for (Map.Entry<String, Integer> entry : entries.entrySet()) {
            String studentId = entry.getKey();
            Integer stu_status = entry.getValue();

            QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("student_id",studentId);

            Student student = studentMapper.selectOne(queryWrapper);
            if(!ObjectUtils.isEmpty(student)){
                student.setStatus(stu_status);
            }

            if(!ObjectUtils.isEmpty(student)){
                students.add(student);
            }

            if(!"realCount".equals(studentId) && stu_status == 1){
                realCount++;
            }

            if(!"leaveCount".equals(studentId) && stu_status == 2){
                leaveCount++;
            }
        }

        map.put("students",students);
        //访问后实时更新
        redisTemplate.opsForHash().put(String.valueOf(taskId),"realCount",realCount);
        redisTemplate.opsForHash().put(String.valueOf(taskId),"leaveCount",leaveCount);

        map.put("realCount",realCount);
        map.put("leaveCount",leaveCount);

        return map;
    }

    /**
     * 返回任务在的地址信息
     * @param taskId
     * @return
     */
    @Override
    public Map<String, Object> getPosition(Long taskId) {
        Map<String,Object> map = new HashMap<>();

        Task task = baseMapper.selectById(taskId);
        if(ObjectUtils.isEmpty(task)){
            throw new BizException(BizErrorCodeEnum.GET_POSITION_FAIL);
        }

        map.put("latitude",task.getLatitude());
        map.put("longitude",task.getLongitude());
        map.put("location",task.getLocation());
        map.put("locationName",task.getLocationName());
        map.put("startTime",task.getStartTime());
        map.put("endTime",task.getEndTime());

        return map;
    }

    /**
     * 获取学生对应的任务列表
     *
     * @param studentId
     * @return
     */
    @Override
    public List<TaskDetail> getTasksByStudentId(String studentId) {
        //先找到这个学生对应的所有班级
        if(StringUtils.isBlank(studentId)){
            throw new BizException(BizErrorCodeEnum.ID_IS_NULL);
        }

        QueryWrapper<ClazzStudent> clazzStudentQueryWrapper = new QueryWrapper<>();
        clazzStudentQueryWrapper.eq("student_id",studentId);

        List<ClazzStudent> list = clazzStudentMapper.selectList(clazzStudentQueryWrapper);

        List<TaskDetail> tasks = new LinkedList<>();

        for (ClazzStudent clazzStudent : list) {
            if(StringUtils.isNotBlank(clazzStudent.getClazzId())){
                List<Task> tmp = getTaskLists(clazzStudent.getClazzId(), 1, Integer.MAX_VALUE);
                tasks.addAll(packageTask(tmp,studentId));
            }
        }

        return tasks;
    }

    /**
     * 获取指定班级下的任务列表
     *
     * @param studentId
     * @param clazzId
     * @return
     */
    @Override
    public List<TaskDetail> getTasksByStudentIdAndClazzId(String studentId, String clazzId,Integer page,Integer size) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("clazz_id",clazzId);
        queryWrapper.orderByDesc("start_time");

        Page<Task> pages = new Page<>(page,size);
        IPage<Task> iPage = taskMapper.selectPage(pages,queryWrapper);

        List<Task> tasks = iPage.getRecords();

        if(ObjectUtils.isEmpty(tasks) || tasks.isEmpty()){
            return new LinkedList<>();
        }

        return packageTask(tasks, studentId);
    }

    @Override
    public Integer getTaskCount(String studentId) {
        QueryWrapper<Attendance> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("student_id",studentId);

        Integer count = attendanceMapper.selectCount(queryWrapper);

        return count>0?count:0;
    }

    /**
     * 获取老师的任务数
     * @param teacherId
     * @return
     */
    @Override
    public Integer getTeacherTaskCounts(String teacherId) {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teacher_id",teacherId);

        final Integer count = baseMapper.selectCount(queryWrapper);
        return count>0?count:0;
    }

    /**
     * 获取截止时间之前的任务列表
     * @return
     */
    @Override
    public List<Task> getRecentlyTasks() {
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();

        //增大查找范围,统计还需要
        LocalDateTime startLongTime = LocalDateTime.now().plusYears(-1);
        LocalDateTime endLongTime = LocalDateTime.now().plusYears(1);

        //可以查出在截至范围之内的绝大部分任务，然后进行统计
        queryWrapper.le("end_time",endLongTime);
        queryWrapper.ge("start_time",startLongTime);

        return taskMapper.selectList(queryWrapper);
    }

    @Override
    public List<Task> getTodayTasks() {
        LocalDateTime time = LocalDateTime.now();

        //查询当天的任务
        QueryWrapper<Task> queryWrapper = new QueryWrapper<>();

        queryWrapper.ge("start_time",time);
        queryWrapper.le("end_time",time);

        return taskMapper.selectList(queryWrapper);
    }

    /**
     * 统计步骤如下：
     *      1. 筛选这个班级的最近一周的任务列表
     *      2. 将这些列表中的任务的每次考勤情况统计出来
     *
     * @param clazzList
     * @return
     */
    @Override
    public Map<String, Object> statisticsWeek(List<Clazz> clazzList) {
        Map<String,Object> map = new HashMap<>(16);
        if(ObjectUtils.isEmpty(clazzList) || clazzList.isEmpty()){
            map.put("msg","暂无班级列表");
            return map;
        }

        List<StatisticClazzVo> clazzVos = new LinkedList<>();

        final RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        for (Clazz clazz : clazzList) {
            QueryWrapper<Task> taskQueryWrapper = new QueryWrapper<>();
            taskQueryWrapper.eq("clazz_id",clazz.getClazzId());
            LocalDateTime s_t = LocalDateTime.now().plusWeeks(-1);
            LocalDateTime e_t = LocalDateTime.now();
            //获取最近7天的打卡情况
            taskQueryWrapper.ge("start_time",s_t);
            taskQueryWrapper.le("end_time",e_t);

            List<Task> tasks = taskMapper.selectList(taskQueryWrapper);

            //对每一个任务进行统计
            int count = 1;
            List<StatisticTaskVo> taskVos = new LinkedList<>();
            for(Task task : tasks){
                String msg = "第"+count+"次考勤";
                Integer accessCount = task.getAccessCount();
                Integer realCount = task.getRealCount();
                Integer leaveCount = redisTemplate.<String,Integer>opsForHash().get(String.valueOf(task.getTaskId()),"leaveCount");
                count++;
                if(ObjectUtils.isEmpty(leaveCount)){
                    leaveCount = 0;
                }
                taskVos.add(new StatisticTaskVo(msg,accessCount,realCount,leaveCount));
            }

            StatisticClazzVo statisticClazzVo = new StatisticClazzVo();
            BeanUtils.copyProperties(clazz,statisticClazzVo);
            statisticClazzVo.setTaskVos(taskVos);

            clazzVos.add(statisticClazzVo);
        }

        map.put("clazzList",clazzVos);
        return map;
    }

    /**
     * 获取该学生最近一周的打卡考勤情况
     * @param clazzList
     * @return
     */
    @Override
    public Map<String, Object> getTaskResult(List<Clazz> clazzList,String studentId) {
        Map<String,Object> map = new HashMap<>(16);
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        if(ObjectUtils.isEmpty(clazzList) || clazzList.isEmpty()){
            map.put("msg","暂无班级列表");
        }

        List<Task> tasks = new LinkedList<>();

        LocalDateTime lastWeekTime = LocalDateTime.now().plusWeeks(-1);
        LocalDateTime nowTime = LocalDateTime.now();
        for (Clazz clazz : clazzList){
            if(StringUtils.isNotBlank(clazz.getClazzId())){
                //获取所在班级是否有任务
                QueryWrapper<Task> taskQueryWrapper = new QueryWrapper<>();
                taskQueryWrapper.eq("clazz_id",clazz.getClazzId());
                taskQueryWrapper.ge("start_time",lastWeekTime);
                taskQueryWrapper.le("end_time",nowTime);

                List<Task> tmpTasks = taskMapper.selectList(taskQueryWrapper);

                if(!ObjectUtils.isEmpty(tmpTasks) && !tmpTasks.isEmpty()){
                    tasks.addAll(tmpTasks);
                }
            }
        }

        //对筛选出来的任务进行统计(只统计这个学生的)
        int ok = 0;     //已考勤
        int fail = 0;   //缺勤
        int leave = 0;  //请假数

        for (Task task : tasks){
            if(!ObjectUtils.isEmpty(task.getTaskId())){
                Integer state = redisTemplate.<String, Integer>opsForHash().get(String.valueOf(task.getTaskId()), studentId);
                if(state == 0){
                    fail++;
                }else if(state == 1){
                    ok++;
                }else if(state == 2){
                    leave++;
                }
            }
        }

        map.put("msg","punchCount=>已打卡;absence=>缺勤;leave=>请假数");
        map.put("punchCount",ok);
        map.put("absence",fail);
        map.put("leave",leave);

        return map;
    }

    /**
     * 获取当前学生对应的任务状态
     *
     * 一共有以下几种状态：
     *  - 未打卡
     *  - 审批中
     *  - 审批不通过
     *  - 已请假
     *  - 已打卡
     *
     * @param studentId
     * @param taskId
     * @return
     */
    @Override
    public Map<String, Object> getTaskStatus(String studentId, Long taskId) {
        if(StringUtils.isBlank(studentId) || ObjectUtils.isEmpty(taskId)){
            throw new BizException(BizErrorCodeEnum.ID_IS_NULL);
        }

        Map<String,Object> map = new HashMap<>();
        QueryWrapper<Leave> leaveQueryWrapper = new QueryWrapper<>();
        leaveQueryWrapper.eq("student_id",studentId);
        leaveQueryWrapper.eq("task_id",taskId);

        //查找是否有相对应的请假记录
        final Leave leave = leaveMapper.selectOne(leaveQueryWrapper);

        if(ObjectUtils.isEmpty(leave)){
            //学生未请假：只能有未打卡和已打卡两种情况
            QueryWrapper<Attendance> attendanceQueryWrapper = new QueryWrapper<>();
            attendanceQueryWrapper.eq("student_id",studentId);
            attendanceQueryWrapper.eq("task_id",taskId);

            final Attendance attendance = attendanceMapper.selectOne(attendanceQueryWrapper);
            final Integer status = attendance.getStatus();

            if(ObjectUtils.isEmpty(status)){
                throw new BizException(BizErrorCodeEnum.GET_STATUS_ERROR);
            }

            if(status == 1){
                map.put("msg","已打卡");
            }else if(status == 0){
                map.put("msg","未打卡");
            }else if(status == 2){
                map.put("msg","已请假");
            }

            return map;
        }else {
            //学生已请假，看审批状态
            final Integer status = leave.getStatus();
            if(ObjectUtils.isEmpty(status)){
                map.put("msg","审批中");
                return map;
            }

            if(status == 1){
                map.put("msg","已请假");
            }else {
                map.put("msg","审批不通过");
            }
            return map;
        }
    }


    private List<TaskDetail> packageTask(List<Task> tasks,String studentId){
        List<TaskDetail> result = new LinkedList<>();

        if(ObjectUtils.isEmpty(tasks) || tasks.size() == 0){
            return new LinkedList<>();
        }

        for (Task task : tasks) {
            TaskDetail taskDetail = new TaskDetail();
            BeanUtils.copyProperties(task,taskDetail);

            QueryWrapper<Clazz> clazzQueryWrapper = new QueryWrapper<>();
            clazzQueryWrapper.eq("clazz_id",task.getClazzId());
            taskDetail.setClazzName(clazzMapper.selectOne(clazzQueryWrapper).getClazzName());

            QueryWrapper<Teacher> teacherQueryWrapper = new QueryWrapper<>();
            teacherQueryWrapper.eq("teacher_id",task.getTeacherId());
            taskDetail.setTeacherName(teacherMapper.selectOne(teacherQueryWrapper).getRealName());

            QueryWrapper<Attendance> attendanceQueryWrapper = new QueryWrapper<>();
            attendanceQueryWrapper.eq("task_id",task.getTaskId());
            attendanceQueryWrapper.eq("student_id",studentId);
            taskDetail.setStatus(attendanceMapper.selectOne(attendanceQueryWrapper).getStatus());

            result.add(taskDetail);
        }

        return result;
    }

}